package com.whfc.influx.dao.impl;

import com.whfc.common.result.PageData;
import com.whfc.common.util.DateUtil;
import com.whfc.common.util.JSONUtil;
import com.whfc.env.constant.EnvMeasurement;
import com.whfc.env.dto.AppDeviceEnvLogDTO;
import com.whfc.env.dto.EnvLogDTO;
import com.whfc.env.entity.AppDeviceDustLog;
import com.whfc.influx.dao.AppDeviceEnvLogDao;
import org.influxdb.InfluxDB;
import org.influxdb.dto.BatchPoints;
import org.influxdb.dto.Point;
import org.influxdb.dto.Query;
import org.influxdb.dto.QueryResult;
import org.influxdb.impl.InfluxDBMapper;
import org.influxdb.querybuilder.Select;
import org.influxdb.querybuilder.WhereNested;
import org.influxdb.querybuilder.WhereQueryImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.util.ObjectUtils;

import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;

import static com.whfc.env.constant.EnvMeasurement.*;
import static org.influxdb.querybuilder.BuiltQuery.QueryBuilder.*;

/**
 * @Description:
 * @author: xugcheng
 * @version: 1.0
 * @date: 2021-01-28 9:57
 */
@Repository
public class AppDeviceEnvLogDaoImpl implements AppDeviceEnvLogDao {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 数据库名称
     */
    private static final String database = DATABASE;

    /**
     * 表名
     */
    private static final String measurement = MEASUREMENT;

    /**
     * 保留策略:保存10年数据
     */
    private static final String retentionPolicy = RETENTION_POLICY;

    /**
     * 时间单位:秒
     */
    private static final TimeUnit timeUnit = TimeUnit.SECONDS;

    /**
     * 默认查询字段
     */
    private static String[] COLUMNS = null;

    static {
        COLUMNS = new String[]{
                "dustId", "deviceId", "time", "serverTime",
                "pm25", "pm10", "noise", "envTemp", "envRh",
                "windDirection", "windSpeed", "tsp", "atmos", "ss",
                "pm1", "co", "co2", "h2s", "ch4", "o2"
        };
    }

    @Autowired
    private InfluxDB influxDB;

    @Autowired
    private InfluxDBMapper influxDBMapper;

    @Override
    public boolean insert(AppDeviceDustLog record) {
        try {
            logger.debug("influxdb的insert方法");
            
            // 使用当前时间而不是固定的测试时间
            Date currentTime = new Date();
            AppDeviceDustLog currentRecord = new AppDeviceDustLog();
            BeanUtils.copyProperties(record, currentRecord);
            currentRecord.setTime(currentTime);

            Date originalTime = currentRecord.getTime();
            Date utcTime = new Date(originalTime.getTime());

            AppDeviceDustLog utcRecord = new AppDeviceDustLog();
            BeanUtils.copyProperties(currentRecord, utcRecord);
            utcRecord.setTime(utcTime);
            
            logger.info("时间转换调试 - 当前时间: {}, 转换后UTC时间: {}", originalTime, utcTime);
    
            influxDB.setDatabase(database);
            influxDB.setRetentionPolicy(retentionPolicy);
    
            logger.info("使用数据库: {}, 保留策略: {}", database, retentionPolicy);
    
            Point point = this.buildDataPoint(utcRecord);
    
            // 打印更详细的信息
            logger.info("Generated Point: {}", point.toString());
            logger.info("Line Protocol: {}", point.lineProtocol());
    
            // write 方法会抛出异常，如果没有异常就表示写入成功
            influxDB.write(point);
    
            logger.info("数据插入成功");
            return true;
        } catch (Exception e) {
            logger.error("写入 InfluxDB 失败: {}", e.getMessage(), e);
            return false;
        }
    }

    @Override
    public void batchInsert(List<AppDeviceDustLog> records) {
        logger.debug("influxdb的batchInsert方法");
        BatchPoints.Builder batchBuiler = BatchPoints.builder();
        for (AppDeviceDustLog record : records) {
            Point point = this.buildDataPoint(record);
            batchBuiler.point(point);
        }
        influxDB.setDatabase(database);
        influxDB.setRetentionPolicy(retentionPolicy);
        influxDB.write(batchBuiler.build());
    }

    @Override
    public List<AppDeviceEnvLogDTO> selectByDustIdAndTimeBetween(Integer dustId, Date startTime, Date endTime) {
        logger.debug("influxdb的selectByDustIdAndTimeBetween方法");
    
        try {
            logger.info("查询参数 - dustId: {}, startTime: {}, endTime: {}", dustId, startTime, endTime);
    
            // 修复：使用正确的RFC3339格式，包含时区信息
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
            sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); // 设置为CST时区
            String startTimeStr = sdf.format(startTime);
            String endTimeStr = sdf.format(endTime);
    
            logger.info("时间转换 - CST: {}~{} -> RFC3339: {}~{}",
                    startTime, endTime, startTimeStr, endTimeStr);
    
            String sql = String.format(
                    "SELECT dustId, time, ch4, co, h2s, o2, serverTime, ts " +
                            "FROM \"%s\".\"%s\".\"%s\" " +
                            "WHERE dustId = %d " +
                            "AND time >= '%s' " +
                            "AND time <= '%s' " +
                            "ORDER BY time ASC",
                    DATABASE, RETENTION_POLICY, MEASUREMENT, dustId, startTimeStr, endTimeStr
            );
    
            logger.info("执行SQL: {}", sql);
    
            Query query = new Query(sql, DATABASE);
            List<AppDeviceEnvLogDTO> result = influxDBMapper.query(query, AppDeviceEnvLogDTO.class);
            this.transformTime(result);
            logger.info("查询成功，返回 {} 条数据", result.size());
            return result;
    
        } catch (Exception e) {
            logger.error("查询失败: {}", e.getMessage());
            return new ArrayList<>();
        }
    }

    @Override
    public List<AppDeviceEnvLogDTO> selectByDustIdAndTimeBetween(Integer dustId, Date startTime, Date endTime, List<String> columnList) {
        try {
            logger.debug("influxdb的selectByDustIdAndTimeBetween方法");
    
            // 修复：使用正确的RFC3339格式，包含时区信息
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
            sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); // 设置为CST时区
            String startTimeStr = sdf.format(startTime);
            String endTimeStr = sdf.format(endTime);
    
            logger.info("时间转换 - CST: {}~{} -> RFC3339: {}~{}",
                    startTime, endTime, startTimeStr, endTimeStr);
    
            // 直接使用原生SQL（最可靠）
            String sql = String.format(
                    "SELECT dustId, time, ch4, co, h2s, o2, serverTime, ts " +
                            "FROM \"%s\".\"%s\".\"%s\" " +
                            "WHERE dustId = %d " +
                            "AND time >= '%s' " +
                            "AND time <= '%s' " +
                            "ORDER BY time ASC",
                    DATABASE, RETENTION_POLICY, MEASUREMENT, dustId, startTimeStr, endTimeStr);
    
            logger.info("最终查询SQL: {}", sql);
    
            Query query = new Query(sql);
            List<AppDeviceEnvLogDTO> list = influxDBMapper.query(query, AppDeviceEnvLogDTO.class);
            this.transformTime(list);
            logger.info("✅ 查询成功，返回 {} 条数据", list.size());
            return list;
    
        } catch (Exception e) {
            logger.error("❌ 查询失败: {}", e.getMessage());
            return new ArrayList<>();
        }
    }

    @Override
    public PageData<AppDeviceEnvLogDTO> selectByDustIdAndTimeBetween(Integer dustId, Date startTime, Date endTime, Integer pageNum, Integer pageSize) {
        logger.debug("influxdb的selectHelmetDataLogListByDeviceId方法");
        String[] columns = COLUMNS;
    
        // 修复：使用正确的RFC3339格式，包含时区信息
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
        sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); // 设置为CST时区
        String startTimeStr = sdf.format(startTime);
        String endTimeStr = sdf.format(endTime);
    
        logger.info("时间戳调试 - dustId: {}, CST startTime: {} (RFC3339: {}), CST endTime: {} (RFC3339: {})",
                dustId, startTime, startTimeStr, endTime, endTimeStr);
        
        //统计总数量
        Query countQuery = select().count("serverTime")
                .from(database, measurement)
                .where(eq("dustId", String.valueOf(dustId)))
                .and(gte("time", startTimeStr))  // 使用RFC3339格式字符串
                .and(lte("time", endTimeStr));   // 使用RFC3339格式字符串
        logger.info(countQuery.getCommand());
        QueryResult queryResult = influxDB.query(countQuery, TimeUnit.MILLISECONDS);
        Map<String, Object> resultMap = this.parseQueryResultMap(queryResult);
        int total = Double.valueOf(resultMap.getOrDefault("count", 0).toString()).intValue();
        logger.info("数量总数量total={}", total);
    
        //分页查询
        int pages = (total / pageSize) + ((total % pageSize) == 0 ? 0 : 1);
        int offset = (pageNum - 1) * pageSize;
        int limit = pageSize;
        List<AppDeviceEnvLogDTO> list = Collections.EMPTY_LIST;
        if (pageNum <= pages) {
            Select select = select(columns).from(database, measurement)
                    .where(eq("dustId", String.valueOf(dustId)))
                    .andNested()
                    .and(gte("time", startTimeStr))  // 使用CST时间格式字符串
                    .and(lte("time", endTimeStr))    // 使用CST时间格式字符串
                    .close()
                    .orderBy(desc());
            Query query = offset == 0 ? select.limit(limit) : select.limit(limit, offset);
            logger.info(query.getCommand());
            list = influxDBMapper.query(query, AppDeviceEnvLogDTO.class);
            logger.info("查询结果数量: {}", list.size());
            this.transformTime(list);
        }
    
        PageData<AppDeviceEnvLogDTO> pageData = new PageData<>();
        pageData.setList(list);
        pageData.setTotal((long) total);
        pageData.setPages(pages);
        pageData.setPageNum(pageNum);
        pageData.setPageSize(pageSize);
        return pageData;
    }

    @Override
    public EnvLogDTO envLog(List<Integer> dustIdList, Date date) {

        Date startTime = DateUtil.getDateBegin(date);
        Date endTime = DateUtil.getDateEnd(date);

        StringBuilder raw = new StringBuilder();
        raw.append("max(pm25) as pm25Max").append(",min(pm25) as pm25Min");
        raw.append(",max(pm10) as pm10Max").append(",min(pm10) as pm10Min");
        raw.append(",max(noise) as noiseMax").append(",min(noise) as noiseMin");
        raw.append(",max(envTemp) as envTempMax").append(",min(envTemp) as envTempMin");
        raw.append(",max(envRh) as envRhMax").append(",min(envRh) as envRhMin");
        raw.append(",max(windLevel) as windLevelMax").append(",min(windLevel) as windLevelMin");
        raw.append(",max(windSpeed) as windSpeedMax").append(",min(windSpeed) as windSpeedMin");
        raw.append(",max(tsp) as tspMax").append(",min(tsp) as tspMin");
        raw.append(",max(atmos) as atmosMax").append(",min(atmos) as atmosMin");
        raw.append(",max(ss) as ssMax").append(",min(ss) as ssMin");
        raw.append(",max(pm1) as pm1Max").append(",min(pm1) as pm1Min");
        raw.append(",max(co) as coMax").append(",min(co) as coMin");
        raw.append(",max(co2) as co2Max").append(",min(co2) as co2Min");
        raw.append(",max(h2s) as h2sMax").append(",min(h2s) as h2sMin");
        raw.append(",max(ch4) as ch4Max").append(",min(ch4) as ch4Min");
        raw.append(",max(o2) as o2Max").append(",min(o2) as o2Min");

        WhereQueryImpl where = select()
                .raw(raw.toString())
                .from(database, measurement)
                .where();
        WhereNested whereNested = where.andNested();
        for (Integer dustId : dustIdList) {
            whereNested.or(eq("dustId", String.valueOf(dustId)));
        }
        whereNested.close();

        // 修复：使用正确的RFC3339格式，包含时区信息
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
        sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); // 设置为CST时区
        String startTimeStr = sdf.format(startTime);
        String endTimeStr = sdf.format(endTime);

        Query query = where
                .and(gte("time", startTimeStr))  // 使用RFC3339格式
                .and(lte("time", endTimeStr))    // 使用RFC3339格式
                .orderBy(desc());
        logger.info(query.getCommand());
        QueryResult queryResult = influxDB.query(query, TimeUnit.MILLISECONDS);
        Map<String, Object> resultMap = this.parseQueryResultMap(queryResult);
        String jsonStr = JSONUtil.toString(resultMap);
        logger.info("查询结果,{}", jsonStr);
        EnvLogDTO logDTO = JSONUtil.parseObject(jsonStr, EnvLogDTO.class);
        return logDTO;
    }

    /**
     * 构建数据点
     *
     * @param record
     * @return
     */
    private Point buildDataPoint(AppDeviceDustLog record) {
        logger.debug("influxdb的buildDataPoint方法");
        
//        // 修复时间戳生成逻辑 - 使用record.getTime()的毫秒数转换为秒
//        long recordTimeSeconds = record.getTime().getTime() / 1000;
//
//        logger.info("时间戳调试 - Record毫秒: {}, 转换后秒: {}",
//                record.getTime().getTime(), recordTimeSeconds);
    
//        Point.Builder builder = Point
//                .measurement(measurement)
//                .time(recordTimeSeconds, timeUnit);
        // 统一使用CST时间戳 - 直接使用record.getTime()的毫秒数
        long recordTimeMillis = record.getTime().getTime();

        logger.info("时间戳调试 - Record毫秒: {}, 直接使用CST时间戳",
                recordTimeMillis);

        Point.Builder builder = Point
                .measurement(measurement)
                .time(recordTimeMillis, TimeUnit.MILLISECONDS); // 使用毫秒级时间戳

        if (!ObjectUtils.isEmpty(record.getDeviceId())) {
            builder.addField("deviceId", record.getDeviceId());
        }
        if (!ObjectUtils.isEmpty(record.getDustId())) {
            builder.addField("dustId", record.getDustId());
        }
        if (!ObjectUtils.isEmpty(record.getPm25())) {
            builder.addField("pm25", record.getPm25());
        }
        if (!ObjectUtils.isEmpty(record.getPm10())) {
            builder.addField("pm10", record.getPm10());
        }
        if (!ObjectUtils.isEmpty(record.getNoise())) {
            builder.addField("noise", record.getNoise());
        }
        if (!ObjectUtils.isEmpty(record.getEnvTemp())) {
            builder.addField("envTemp", record.getEnvTemp());
        }
        if (!ObjectUtils.isEmpty(record.getEnvRh())) {
            builder.addField("envRh", record.getEnvRh());
        }
        if (!ObjectUtils.isEmpty(record.getWindDirection())) {
            builder.addField("windDirection", record.getWindDirection());
        }
        if (!ObjectUtils.isEmpty(record.getWindSpeed())) {
            builder.addField("windSpeed", record.getWindSpeed());
        }
        if (!ObjectUtils.isEmpty(record.getTsp())) {
            builder.addField("tsp", record.getTsp());
        }
        if (!ObjectUtils.isEmpty(record.getAtmos())) {
            builder.addField("atmos", record.getAtmos());
        }
        if (!ObjectUtils.isEmpty(record.getSs())) {
            builder.addField("ss", record.getSs());
        }
        if (!ObjectUtils.isEmpty(record.getPm1())) {
            builder.addField("pm1", record.getPm1());
        }
        if (!ObjectUtils.isEmpty(record.getCo())) {
            builder.addField("co", record.getCo());
        }
        if (!ObjectUtils.isEmpty(record.getCo2())) {
            builder.addField("co2", record.getCo2());
        }
        if (!ObjectUtils.isEmpty(record.getH2s())) {
            builder.addField("h2s", record.getH2s());
        }
        if (!ObjectUtils.isEmpty(record.getCh4())) {
            builder.addField("ch4", record.getCh4());
        }
        if (!ObjectUtils.isEmpty(record.getO2())) {
            builder.addField("o2", record.getO2());
        }
        // 修复serverTime和ts字段生成逻辑 - 使用秒级时间戳
        long recordTimeSeconds = recordTimeMillis / 1000;
        builder.addField("serverTime", recordTimeSeconds);
        builder.addField("ts", recordTimeSeconds);
        
        return builder.build();
    }

    /**
     * 时间类型转换
     *
     * @param list
     */
    private void transformTime(List<AppDeviceEnvLogDTO> list) {
        for (AppDeviceEnvLogDTO dto : list) {
            // 统一使用CST时间，不进行时区转换
            if (dto.getDeviceTime() != null) {
                // 直接使用UTC时间，因为InfluxDB存储的是UTC时间
                dto.setTime(Date.from(dto.getDeviceTime()));
            }

            // 设置创建时间 - 使用CST时间
            if (!ObjectUtils.isEmpty(dto.getServerTime())) {
                // serverTime是秒级时间戳，转换为毫秒后创建Date对象
                dto.setCreateTime(new Date(dto.getServerTime() * 1000));
            }
        }
    }

    /**
     * 解析查询结果
     *
     * @param queryResult
     * @return
     */
    private Map<String, Object> parseQueryResultMap(QueryResult queryResult) {
        Map<String, Object> map = null;
        if (queryResult != null) {
            List<QueryResult.Result> results = queryResult.getResults();
            if (results != null) {
                QueryResult.Result result = results.get(0);
                List<QueryResult.Series> seriesList = result.getSeries();
                if (seriesList != null && seriesList.size() > 0) {
                    List<String> columns = seriesList.get(0).getColumns();
                    List<List<Object>> values = seriesList.get(0).getValues();
                    logger.info("columns:{}", columns);
                    logger.info("values:{}", values);
                    if (values != null && values.size() > 0) {
                        List<Object> valueArr = values.get(0);
                        map = new HashMap<>(columns.size());
                        for (int i = 0; i < columns.size(); i++) {
                            String column = columns.get(i);
                            Object value = valueArr.get(i);
                            map.put(column, value);
                        }
                    }

                }
            }
        }
        if (map == null) {
            map = Collections.EMPTY_MAP;
        }
        return map;
    }

    /**
     * 解析查询结果
     *
     * @param queryResult
     * @return
     */
    private List<Map<String, Object>> parseQueryResultList(QueryResult queryResult) {
        List<Map<String, Object>> list = null;
        if (queryResult != null) {
            List<QueryResult.Result> results = queryResult.getResults();
            if (results != null && results.size() > 0) {
                QueryResult.Result result = results.get(0);
                List<QueryResult.Series> seriesList = result.getSeries();
                if (seriesList != null && seriesList.size() > 0) {
                    list = new ArrayList<>(seriesList.size());
                    for (int i = 0; i < seriesList.size(); i++) {

                        QueryResult.Series series = seriesList.get(i);
                        Map<String, String> tags = series.getTags();
                        List<String> columns = series.getColumns();
                        List<List<Object>> values = series.getValues();
                        Map<String, Object> map = new HashMap<>(columns.size() + tags.size());
                        logger.info("tags:{}", tags);
                        logger.info("columns:{}", columns);
                        logger.info("values:{}", values);
                        if (values != null && values.size() > 0) {
                            List<Object> valueArr = values.get(0);
                            for (int j = 0; j < columns.size(); j++) {
                                String column = columns.get(j);
                                Object value = valueArr.get(j);
                                map.put(column, value);
                            }
                        }
                        map.putAll(tags);
                        list.add(map);
                    }
                }
            }
        }
        if (list == null) {
            list = Collections.EMPTY_LIST;
        }
        return list;
    }
}